﻿// Copyright (c) Inossem. All rights reserved.
// Licensed under the LGPL-3.0 license. See LICENSE file in the project root for full license information.

using DWF.Studio.Custom;
using DWF.Studio.ISerivces;
using DWF.Studio.ScriptEngine;
using DWF.Studio.ScriptEngine.Custom;
using DWF.Studio.ScriptEngine.Event;
using DWF.Studio.ScriptEngine.IPC.Msg;
using DWF.Studio.ScriptEngine.IPC.WorkFlow;
using DWF.Studio.ScriptEngine.Loader;
using DWF.Studio.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DWF.Studio.Script.Excutor
{
    internal class RemoteWorkflowScriptExecutor : RemoteScriptExecutorBase
    {
        private IScriptEngineLoader _scriptEngineLoader = null;

        private ScriptEngineProxyContext _engineProxyContext = null;

        private string[] _assemblyNames = null;

        private string[] _searchPaths = null;

        private Func<Dictionary<string, string>> _projectPropertiesFunc = null;

        public RemoteWorkflowScriptExecutor(ScriptEngineProxyContext engineProxyContext)
        {
            _engineProxyContext = engineProxyContext;
            _engineProxyContext.LoadSubEngine += (scriptEngine, engineType, scriptPath) =>
            {
                if (engineType == EEngineType.WorkflowScriptEngine)
                {
                    scriptEngine.ExecuteAborted = (e) => { this.ExecuteAborted?.Invoke(new ScriptExcuteAbortedEventArgs { ExecutionId = scriptEngine.Id, Reason = e.Reason }); };
                    scriptEngine.ExecuteOnUnhandledException = (e) => { this.ExecuteOnUnhandledException?.Invoke(new ScriptExcuteUnhandledExceptionEventArgs { Location = e.Location, ExecutionId = scriptEngine.Id, ScriptPath = e.ScriptPath, TypeName = e.TypeName, StackTrace = e.StackTrace, Message = e.Message }); };
                    scriptEngine.ExecuteCompleted = (e) => { this.ExecuteCompleted?.Invoke(new ScriptExcuteCompletedEventArgs { ExecutionId = scriptEngine.Id, TerminationMessage = e.TerminationMessage }); };
                    scriptEngine.ExcuteOutput = (line) => { Console.Write(line); };
                    scriptEngine.ExecuteTrack = (e) => {
                        this.ExecuteTrack?.Invoke(new ScriptExecuteTrackEventArgs(e.TrackData)); 
                    };
                    scriptEngine.DebugTrack = (e) => { this.DebugTrack?.Invoke(e.FrameInfo); };
                    scriptEngine.HitBreakpoint = (e) => { this.HitBreakpoint?.Invoke(new HitBreakpointEventArgs { ExecutionId = scriptEngine.Id, ScriptPath = e.StepInfo.ScriptPath, Verb = e.Verb, Location = ((WorkflowStepInfo)e.StepInfo).Id }); };
                    scriptEngine.ExecuteStart = (e) => { this.ExecuteStart?.Invoke(new ExecuteStartEventArgs { ExecutionId = scriptEngine.Id }); };
                    scriptEngine.ExecuteEnd = (e) => { this.ExecuteEnd?.Invoke(new ExecuteEndEventArgs { ExecutionId = scriptEngine.Id }); };

                    this.SubScriptExecuteStart?.Invoke(new SubScriptExecuteStartEventArgs { ExecutionId = scriptEngine.Id, ScriptPath = scriptPath });
                }
            };

            _engineProxyContext.UnloadSubEngine = (currentEngineId, unloadEngineId) =>
            {
                this.SubScriptExecuteEnd?.Invoke(new SubScriptExecuteEndEventArgs { ParentExecutionId = currentEngineId, ExecutionId = unloadEngineId });
            };

            _scriptEngineLoader = new WorkflowEngineProcessLoader(_engineProxyContext);
        }

        public override void Cancel()
        {
            if (_engineProxyContext.CurrentEngine != null)
            {
                _engineProxyContext.CurrentEngine.Cancel();
                _scriptEngineLoader.Unload();
            }
        }

        public override string Debug(string projectDirectory, string scriptPath, bool isStepOver)
        {
            return Debug(projectDirectory, scriptPath, null, isStepOver);
        }

        public override string Debug(string projectDirectory, string scriptPath, Dictionary<string, object> param, bool isStepOver)
        {
            IScriptEngine scriptEngine = null;
            var projectProperties = _projectPropertiesFunc();
            var targetPlatform = projectProperties["projectTargetPlatform"];
            //多组启动参数 Oliver 2021-06-24
            base.getStartupProperties(projectProperties, scriptPath);

            if (targetPlatform == StaticResource.TargetPlatform_x86)
            {
                scriptEngine = _scriptEngineLoader.Load(_searchPaths, _assemblyNames, projectProperties, _engineProxyContext.PipeName, ETargetPlatform.x86);
            }
            else if (targetPlatform == StaticResource.TargetPlatform_x64)
            {
                scriptEngine = _scriptEngineLoader.Load(_searchPaths, _assemblyNames, projectProperties, _engineProxyContext.PipeName, ETargetPlatform.x64);
            }
            else
            {
                return null;
            }

            scriptEngine.ExecuteAborted = (e) => { this.ExecuteAborted?.Invoke(new ScriptExcuteAbortedEventArgs { ExecutionId = scriptEngine.Id, Reason = e.Reason }); };
            scriptEngine.ExecuteOnUnhandledException = (e) => { this.ExecuteOnUnhandledException?.Invoke(new ScriptExcuteUnhandledExceptionEventArgs { Location = e.Location, ExecutionId = scriptEngine.Id, ScriptPath = e.ScriptPath, TypeName = e.TypeName, StackTrace = e.StackTrace, Message = e.Message }); };
            scriptEngine.ExecuteCompleted = (e) => { this.ExecuteCompleted?.Invoke(new ScriptExcuteCompletedEventArgs { ExecutionId = scriptEngine.Id, TerminationMessage = e.TerminationMessage }); };
            scriptEngine.ExcuteOutput = (line) => { Console.Write(line); };
            scriptEngine.ExecuteTrack = (e) => { this.ExecuteTrack?.Invoke(new ScriptExecuteTrackEventArgs(e.TrackData)); };
            scriptEngine.DebugTrack = (e) => { this.DebugTrack?.Invoke(e.FrameInfo); };
            scriptEngine.HitBreakpoint = (e) => { this.HitBreakpoint?.Invoke(new HitBreakpointEventArgs { ExecutionId = scriptEngine.Id, ScriptPath = e.StepInfo.ScriptPath, Verb = e.Verb, Location = ((WorkflowStepInfo)e.StepInfo).Id }); };
            scriptEngine.ExecuteStart = (e) => { this.ExecuteStart?.Invoke(new ExecuteStartEventArgs { ExecutionId = scriptEngine.Id }); };
            scriptEngine.ExecuteEnd = (e) => { this.ExecuteEnd?.Invoke(new ExecuteEndEventArgs { ExecutionId = scriptEngine.Id }); };

            _engineProxyContext.SetDebug(isStepOver);
            scriptEngine.DebugScript(projectDirectory, scriptPath, param);

            return scriptEngine.Id;
        }

        public override string Execute(string projectDirectory, string scriptPath)
        {
            return Execute(projectDirectory, scriptPath, null);
        }

        public override string Execute(string projectDirectory, string scriptPath, Dictionary<string, object> param)
        {
            IScriptEngine scriptEngine = null;
            var projectProperties = _projectPropertiesFunc();
            var targetPlatform = projectProperties["projectTargetPlatform"];
            //多组启动参数 Oliver 2021-06-24
            base.getStartupProperties(projectProperties, scriptPath);

            if (targetPlatform == StaticResource.TargetPlatform_x86)
            {
                scriptEngine = _scriptEngineLoader.Load(_searchPaths, _assemblyNames, projectProperties, _engineProxyContext.PipeName, ETargetPlatform.x86);
            }
            else if (targetPlatform == StaticResource.TargetPlatform_x64)
            {
                scriptEngine = _scriptEngineLoader.Load(_searchPaths, _assemblyNames, projectProperties, _engineProxyContext.PipeName, ETargetPlatform.x64);
            }
            else
            {
                return null;
            }

            scriptEngine.ExecuteAborted = (e) => { this.ExecuteAborted?.Invoke(new ScriptExcuteAbortedEventArgs { ExecutionId = scriptEngine.Id, Reason = e.Reason }); };
            scriptEngine.ExecuteOnUnhandledException = (e) => { this.ExecuteOnUnhandledException?.Invoke(new ScriptExcuteUnhandledExceptionEventArgs { Location = e.Location, ExecutionId = scriptEngine.Id, ScriptPath = e.ScriptPath, TypeName = e.TypeName, StackTrace = e.StackTrace, Message = e.Message }); };
            scriptEngine.ExecuteCompleted = (e) => { this.ExecuteCompleted?.Invoke(new ScriptExcuteCompletedEventArgs { ExecutionId = scriptEngine.Id, TerminationMessage = e.TerminationMessage }); };
            scriptEngine.ExcuteOutput = (line) => { Console.Write(line); };
            scriptEngine.ExecuteTrack = (e) => { this.ExecuteTrack?.Invoke(new ScriptExecuteTrackEventArgs(e.TrackData)); };
            scriptEngine.DebugTrack = (e) => { this.DebugTrack?.Invoke(e.FrameInfo); };
            scriptEngine.ExecuteStart = (e) => { this.ExecuteStart?.Invoke(new ExecuteStartEventArgs { ExecutionId = scriptEngine.Id }); };
            scriptEngine.ExecuteEnd = (e) => { this.ExecuteEnd?.Invoke(new ExecuteEndEventArgs { ExecutionId = scriptEngine.Id }); };

            scriptEngine.ExecuteScript(projectDirectory, scriptPath, param);

            return scriptEngine.Id;
        }

        public override void DebugResume()
        {
            _engineProxyContext.CurrentEngine?.DebugResume();
        }

        public override void DebugStepInto()
        {
            _engineProxyContext.CurrentEngine?.DebugStepInto();
        }

        public override void DebugStepOut()
        {
            throw new NotImplementedException();
        }

        public override void DebugStepOver()
        {
            _engineProxyContext.CurrentEngine?.DebugStepOver();
        }

        public override void RefreshBreakpoint(string scriptPath)
        {
            _engineProxyContext.RefreshBreakpoints(scriptPath);
        }

        public override void CallDebugInfos()
        {
            _engineProxyContext.CurrentEngine.CallFrameInfo();
        }

        public override void SetLoadAssemblyNames(string[] assemblyNames)
        {
            _assemblyNames = assemblyNames;
        }

        public override void SetSearchPaths(string[] paths)
        {
            _searchPaths = paths;
        }

        public override void SetProjectProperties(Func<Dictionary<string, string>> projectPropertiesFunc)
        {
            _projectPropertiesFunc = projectPropertiesFunc;
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                    _engineProxyContext.Dispose();
                    _engineProxyContext = null;
                    _scriptEngineLoader = null;
                    _assemblyNames = null;
                    _searchPaths = null;
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~RemoteWorkflowScriptExcutor()
        // {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public override void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion
    }
}
